/** 自定义文件上传 
 * @description: 自定义文件上传
 * @param {UploadConfig} config
 * @returns {UploadHookReturn}
 * @Date: 2025-05-20 16:18:51
 * @Author: cy
*/

import { ref, type Ref } from "vue";

export interface UploadConfig {
  url?: string; // 上传接口地址
  method?: "POST" | "PUT"; // 请求方法
  fieldName?: string; // 文件字段名称
  headers?: Record<string, any>; // 自定义请求头
  maxFileSize?: number; // 最大文件大小（字节）
  allowedFileTypes?: string[]; // 允许的文件类型
  multiple?: boolean; // 是否支持多文件
  withCredentials?: boolean; // 是否携带凭证
  busType?: string;
  customUploader?: (
    file: File,
    context: {
      reportProgress?: (progress: number) => void;
      formData: FormData;
      config?: UploadConfig;
    }
  ) => Promise<any>;
}

export interface UploadFile {
  file: File;
  progress: number; // 上传进度 0-100
  status: "pending" | "uploading" | "success" | "error";
  response?: any; // 服务器响应
  error?: Error;
}

export interface UploadHookReturn {
  files: Ref<UploadFile[]>;
  isUploading: Ref<boolean>;
  error: Ref<Error | null>;
  uploadProgress: Ref<number>; // 整体上传进度
  selectFiles: () => void;
  upload: (files?: FileList) => Promise<void>;
  reset: () => void;
}

export function useFileUpload(config: UploadConfig): UploadHookReturn {
  const files = ref<UploadFile[]>([]) as Ref<UploadFile[]>;
  const isUploading = ref(false);
  const error = ref<Error | null>(null);
  const uploadProgress = ref(0);

  // 创建隐藏的 input 元素
  const createFileInput = () => {
    const input = document.createElement("input");
    input.type = "file";
    input.style.display = "none";
    input.multiple = config.multiple || false;
    if (config.allowedFileTypes) {
      input.accept = config.allowedFileTypes.join(",");
    }
    return input;
  };

  // 文件选择处理
  const selectFiles = () => {
    const input = createFileInput();
    input.addEventListener("change", async (e) => {
      const selectedFiles = (e.target as HTMLInputElement).files;
      if (selectedFiles) {
        await upload(selectedFiles);
      }
      document.body.removeChild(input);
    });

    document.body.appendChild(input);
    input.click();
  };

  // 验证文件
  const validateFile = (file: File): boolean => {
    if (config.maxFileSize && file.size > config.maxFileSize) {
      throw new Error(`文件大小不能超过 ${config.maxFileSize / 1024 / 1024}MB`);
    }

    if (
      config.allowedFileTypes &&
      !config.allowedFileTypes.includes(file.type)
    ) {
      throw new Error(`不支持的文件类型: ${file.type}`);
    }

    return true;
  };

  // 执行上传
  const upload = async (selectedFiles?: FileList) => {
    if (!selectedFiles || selectedFiles.length === 0) return;

    error.value = null;
    isUploading.value = true;

    // 初始化文件列表
    const newFiles = Array.from(selectedFiles).map((file) => ({
      file,
      progress: 0,
      status: "pending" as const,
      response: undefined,
      error: undefined,
    }));

    files.value = config.multiple ? [...files.value, ...newFiles] : newFiles;

    try {
      await Promise.all(newFiles.map(processFileUpload));
    } catch (err) {
      error.value = err instanceof Error ? err : new Error("上传失败");
    } finally {
      isUploading.value = false;
    }
  };

  // 处理单个文件上传
  const processFileUpload = async (uploadFile: UploadFile) => {
    const index = files.value.findIndex((f) => f.file === uploadFile.file);

    try {
      validateFile(uploadFile.file);

      const formData = new FormData();
      formData.append("busType", config.busType || "36");
      formData.append(config.fieldName || "file", uploadFile.file);

      if (config.customUploader) {
        files.value[index].status = "uploading";

        const response = await config.customUploader(uploadFile.file, {
          reportProgress: (e: any) => {
            const progress = Math.round((e?.loaded / e?.total) * 100);
            files.value[index].progress = progress;
            updateOverallProgress();
          },
          formData,
        });
        files.value[index].status = "success";
        files.value[index].response = response;
        return;
      }
      return new Promise<void>((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.upload.addEventListener("progress", (e) => {
          if (e.lengthComputable) {
            const progress = Math.round((e.loaded / e.total) * 100);
            files.value[index].progress = progress;
            updateOverallProgress();
          }
        });

        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              files.value[index].status = "success";
              files.value[index].response = JSON.parse(xhr.responseText);

              resolve();
            } else {
              handleUploadError(index, xhr);
              reject(new Error(`上传失败: ${xhr.statusText}`));
            }
          }
        };

        xhr.onerror = () => {
          handleUploadError(index, xhr);
          reject(new Error("网络错误"));
        };

        xhr.open(config.method || "POST", config?.url as string, true);

        // 设置请求头
        if (config.headers) {
          Object.entries(config.headers).forEach(([key, value]) => {
            xhr.setRequestHeader(key, value);
          });
        }

        if (config.withCredentials) {
          xhr.withCredentials = true;
        }

        files.value[index].status = "uploading";
        xhr.send(formData);
      });
    } catch (err) {
      handleUploadError(index, err);
      throw err;
    }
  };

  // 更新整体进度
  const updateOverallProgress = () => {
    const total = files.value.reduce((sum, f) => sum + f.progress, 0);
    uploadProgress.value = Math.round(total / files.value.length);
  };

  // 处理上传错误
  const handleUploadError = (index: number, error: any) => {
    files.value[index].status = "error";
    files.value[index].error =
      error instanceof Error ? error : new Error(error.message || "上传失败");
  };

  // 重置状态
  const reset = () => {
    files.value = [];
    isUploading.value = false;
    error.value = null;
    uploadProgress.value = 0;
  };

  return {
    files,
    isUploading,
    error,
    uploadProgress,
    selectFiles,
    upload,
    reset,
  };
}
